#if !defined(SSMUTEX_H_INCLUDED)
#define SSMUTEX_H_INCLUDED

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#define AUTO_MUTEX(classT)						SSMutexT<##classT, TRUE>::AutoLock lock##classT(_T("##classT"));
#define AUTO_MUTEX_TIME(classT, Milliseconds)	SSMutexT<##classT, TRUE>::AutoLock lock##classT(_T("##classT"), (DWORD)(Milliseconds));

/**
 *	template<class T> class SSMutexT
 *
 *	The SmartSingletonMutex class
 *
 *	1) one and only one instance in a global address space of a process (OOP friendly global variable)
 *	2) lazy instantiation(intantiated only if it is used first time)
 *	3) auto delete(cleanup)
 *	4) auto delete mutex support
 *
*/

#define AUTO_MUTEX_INTERNAL_USE_ONLY(classT)	SSMutexT<T, t_bAutoMutex>::AutoLock lock##classT(_T("##classT"));

template<class T, BOOL t_bAutoMutex = FALSE>
class SSMutexT
{
public:
	class AutoLock
	{
	private:
		LPCTSTR m_szNamedMutex;

	public:
		AutoLock(LPCTSTR szNamedMutex, DWORD dwMilliseconds = 10000) : m_szNamedMutex(szNamedMutex)
		{
			HANDLE hMutex = SSMutexT<T, t_bAutoMutex>::GetMutex(m_szNamedMutex);
			if (hMutex)
			{

#if !defined(_DEBUG)

				::WaitForSingleObject(hMutex, dwMilliseconds);

#else	// #if !defined(_DEBUG)


				DWORD dwResult = ::WaitForSingleObject(hMutex, dwMilliseconds);
				switch(dwResult)
				{
				case WAIT_OBJECT_0:
					break;
					
				case WAIT_TIMEOUT:
					TRACE(_T("Object synchronization Error - WAIT_TIMEOUT\n"));
					break;

				case WAIT_ABANDONED:
					TRACE(_T("Object synchronization Error - WAIT_ABANDONED\n"));
					break;

				case WAIT_FAILED:
					{
						DWORD dwErr = ::GetLastError();
						LPTSTR sErrMsg;
						if (::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
							NULL,										// no source buffer needed
							dwErr,										// error code for this message
							MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),	// default language ID
							(LPTSTR)&sErrMsg,							// allocated by fcn
							0,											// Minimum size of buffer
							NULL										// no inserts
							) == 0)
						{
							// Unknown error code
							TRACE(_T("Object synchronization Error - WAIT_FAILED\nUnknown error 0x%08x (%d)\n"), dwErr, ((WORD)((DWORD_PTR)(dwErr) & 0xffff)));
						} /* failed */
						else
						{
							TRACE(_T("Object synchronization Error - WAIT_FAILED\n%s"), sErrMsg);
							::LocalFree(sErrMsg);
						}
					}
					break;
				}

#endif	// #if !defined(_DEBUG)

			}
		}
		
		~AutoLock()
		{
			HANDLE hMutex = SSMutexT<T, t_bAutoMutex>::GetMutex(m_szNamedMutex);
			if ( hMutex )
				::ReleaseMutex(hMutex);
		}
	};

	inline static SSMutexT<T, t_bAutoMutex> &Instance(LPCTSTR szNamedMutex = NULL)
	{
		static SSMutexT<T, t_bAutoMutex> instance(szNamedMutex);
		return instance;
	}

	inline T * operator -> ()
	{
		AUTO_MUTEX_INTERNAL_USE_ONLY(T);

		return Get();
	}

	inline operator T * ()
	{
		AUTO_MUTEX_INTERNAL_USE_ONLY(T);

		return Get();
	}

	inline T & operator * ()
	{
		AUTO_MUTEX_INTERNAL_USE_ONLY(T);

		return *Get();
	}

	inline T ** operator & ()
	{
		AUTO_MUTEX_INTERNAL_USE_ONLY(T);

		return &Get();
	}
	
	inline void Delete()
	{
		AUTO_MUTEX_INTERNAL_USE_ONLY(T);
		
		delete m_pInstance;
		m_pInstance = NULL;
	}

public:
	/// auto delete (clenaup)
	~SSMutexT()
	{
		delete m_pInstance;

		if (t_bAutoMutex && m_hMutex)
		{
			::CloseHandle(m_hMutex);
			m_hMutex = NULL;
		}
	}

	inline static HANDLE GetMutex(LPCTSTR szNamedMutex)
	{
		return SSMutexT<T, t_bAutoMutex>::Instance(szNamedMutex).m_hMutex;
	}

protected:
	SSMutexT(LPCTSTR szNamedMutex = NULL) : m_pInstance(NULL), m_hMutex(NULL)
	{
		if(t_bAutoMutex)
			m_hMutex = ::CreateMutex(NULL, FALSE, szNamedMutex);
	}

	SSMutexT(SSMutexT<T, t_bAutoMutex> &rRhs) { }
	SSMutexT<T, t_bAutoMutex> operator = (SSMutexT<T, t_bAutoMutex> &rRhs) { return *this; }

	inline T *Get()
	{
		AUTO_MUTEX_INTERNAL_USE_ONLY(T);

		if (m_pInstance == NULL)
			m_pInstance = new T;

		return m_pInstance;
	}

private:
	HANDLE m_hMutex;
	T *m_pInstance;
};

#endif	// #if !defined(SSMUTEX_H_INCLUDED)